package org.gis.utils.dps;

import com.thoughtworks.xstream.converters.ConversionException;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import java.lang.reflect.Constructor;
import java.util.Enumeration;

/**
 *
 * @author gus
 */
class DynamicPropertySetConverter implements Converter
{
    private Class baseType;
    public DynamicPropertySetConverter(Class<? extends DynamicPropertySet> type)
    {
        this.baseType = type;
    }
    
    public void marshal( Object object, HierarchicalStreamWriter writer, MarshallingContext context )
    {
        DynamicPropertySet bean = (DynamicPropertySet) object;
        internalMarshal(bean, writer, context);
    }

    private  DynamicPropertySet  newInstance() throws Exception
    {
        Constructor ctor = baseType.getConstructor(new Class[0]);
        return  (DynamicPropertySet) ctor.newInstance(new Object[0]);
    }
    
    public Object unmarshal( HierarchicalStreamReader reader, UnmarshallingContext context )
    {
        try
        {
            DynamicPropertySet bean = newInstance();
            internalUnmarshal(bean, reader, context);
            return bean;
        } catch (Exception ex)
        {
            reader.appendErrors(new ConversionException(ex));
        }
        return null;
    }

    public boolean canConvert( Class type )
    {
        return isDynamicPropertySetAssignable(type);
    }

    private String extractValue( HierarchicalStreamReader reader)
    {
        reader.moveDown();
        String value = reader.getValue(); 
        reader.moveUp();
        return value;
    }

    private void internalUnmarshal( DynamicPropertySet bean, HierarchicalStreamReader reader, UnmarshallingContext context ) throws Exception
    {
        DynamicProperty property = null;
        while(reader.hasMoreChildren())
        {
            reader.moveDown(); // property
            
            String propName = reader.getAttribute("name");
            Class propType  =  Class.forName(reader.getAttribute("type"));
            property = new DynamicProperty(propName, propType);

            unmarshalAttributes(property, reader, context);
           
            if( isDynamicPropertySetAssignable(property.getType()) )
            {
                reader.moveDown();
                reader.moveDown();
                DynamicPropertySet innerBean = newInstance();
                internalUnmarshal(innerBean, reader, context);
                property.setValue(innerBean);
                reader.moveUp();
                reader.moveUp();
            } else
            {
                reader.moveDown();
                property.setValue(context.convertAnother(property, property.getType()));
                reader.moveUp();
            }
            reader.moveUp();
            bean.add(property);
        }
        
    }
    
    private boolean isDynamicPropertySetAssignable(Class type)
    {
        return DynamicPropertySet.class.isAssignableFrom(type);
    }
    
    
    private void internalMarshal( DynamicPropertySet bean, HierarchicalStreamWriter writer, MarshallingContext context )
    {
        for(DynamicProperty  property : bean)
        {
            writer.startNode("DynamicProperty");
            writer.addAttribute("name", property.getName());
            writer.addAttribute("type", property.getType().getName());

            marshalAttributes(property, context, writer);
            
            writer.startNode("value");
            if( isDynamicPropertySetAssignable(property.getType()) )
            {
                writer.startNode("DynamicPropertySet");
                internalMarshal((DynamicPropertySet)property.getValue(), writer, context);
                writer.endNode();
            } else
            {
                context.convertAnother(property.getValue());
            }
            writer.endNode();
            writer.endNode();
        }
    }

    private void marshalAttributes( DynamicProperty property, MarshallingContext context, HierarchicalStreamWriter writer )
    {
        Enumeration<String> attributes = property.getDescriptor().attributeNames();
        writer.startNode("attributes");

        while ( attributes.hasMoreElements() )
        {
            String name = attributes.nextElement();
            writer.startNode("attribute");
            Object attribute = property.getAttribute(name);
            writer.addAttribute("name", name);
            writer.addAttribute("type", attribute.getClass().getName());
            context.convertAnother(attribute);
            writer.endNode();
        }
        writer.endNode();
    }

    private void unmarshalAttributes(DynamicProperty property, HierarchicalStreamReader reader, UnmarshallingContext context ) throws Exception
    {
        reader.moveDown(); // attributes
        while(reader.hasMoreChildren())
        {
            String name = reader.getAttribute("name");
            Class attributeType = Class.forName(reader.getAttribute("type"));
            Object value = context.convertAnother(property, attributeType);
            property.setAttribute(name, value);
        }
        reader.moveUp();
    }
}
